#
# This file is a part of the open-eBackup project.
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at
# http://mozilla.org/MPL/2.0/.
#
# Copyright (c) [2024] Huawei Technologies Co.,Ltd.
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
#

import os
import sqlite3
import time
import uuid
import json

from adds.comm.log import log
from adds.schemas.adds_schemas import SqliteModel

dir_type_list = [
    "container", "fileLinkTracking", "domainPolicy", "groupPolicyContainer", "builtinDomain",
    "organizationalUnit", "dnsZone", "msDFSR-GlobalSettings", "msDFSR-ReplicationGroup", "lostAndFound",
    "msDFSR-Content", "msDFSR-Topology", "msDFSR-LocalSettings", "msDFSR-Subscriber", "msImaging-PSPs",
    "msDS-ShadowPrincipalContainer", "msDS-QuotaContainer", "msTPM-InformationObjectsContainer"
]


class ADSqliteService:
    @staticmethod
    def write_metadata_to_sqlite_file(path, data_params, parent_id_map):
        try:
            sqlite_path = os.path.join(path, "sqlite")
            if not os.path.exists(sqlite_path):
                os.makedirs(sqlite_path)
            ADSqliteService.init_db(sqlite_path, data_params, parent_id_map)
        except Exception as err:
            log.error(f"write metadata to sqlite file failed,err:{err}")

    @staticmethod
    def init_db(path, data_params, parent_id_map):
        sqlite_db_name = "copymetadata.sqlite"
        db_path = os.path.join(path, sqlite_db_name)
        conn = sqlite3.connect(db_path)
        # 创建数据库表
        t_copy_metadata = "T_COPY_METADATA"
        create_sql_table = ADSqliteService.create_table_struct(t_copy_metadata)
        conn.execute(create_sql_table)
        conn.commit()

        # 数据库信息是否已经存在，用NAME+TYPE字段过滤，不存在则插入数据库结构信息
        statement = f"INSERT OR IGNORE INTO {t_copy_metadata} VALUES(?,?,?,?,?,?,?,?,?,?,?,?)"
        if data_params is not None:
            database_data = ADSqliteService.init_sql_params(data_params, parent_id_map)
            conn.executemany(statement, database_data)
            conn.commit()
        conn.close()

    @staticmethod
    def create_table_struct(t_copy_metadata):
        return f'''CREATE TABLE IF NOT EXISTS '{t_copy_metadata}'(
            UUID varchar(512) PRIMARY KEY NOT NULL,
            NAME TEXT NOT NULL,
            TYPE varchar(32) NOT NULL,
            PARENT_PATH TEXT NOT NULL,
            PARENT_UUID varchar(512),
            SIZE BIGINT,
            CREATE_TIME varchar(64),
            MODIFY_TIME varchar(64),
            EXTEND_INFO TEXT,
            OBJECT_INFO TEXT,
            RES_TYPE varchar (64),
            RES_SUB_TYPE varchar (64));'''

    @staticmethod
    def init_sql_params(data_params, parent_id_map):
        data_list = []
        for data_param in data_params:
            if data_param.get("ObjectClass", "") == "domainDNS":
                parent_id_map["/" + data_param.get("CanonicalName", "")[:-1]] = data_param.get("ObjectGUID", "")
                continue
            param = ADSqliteService.format_param(data_param, parent_id_map)
            data_list.append(
                (param.uuid, param.name, param.type, param.parent_path, param.parent_uuid, param.size,
                 param.create_time, param.modify_time, param.extend_info, param.object_info, param.res_type,
                 param.res_sub_type))
        return data_list

    @staticmethod
    def format_param(data_param, parent_id_map):
        param = SqliteModel()
        param.name = data_param.get("Name", "")
        param.uuid = data_param.get("ObjectGUID", "")
        extend_info = {
            "name": data_param.get("CanonicalName", ""), "description": data_param.get("Description", ""),
            "type": data_param.get("ObjectClass", "")
        }
        param.extend_info = json.dumps(extend_info, ensure_ascii=False)
        param.object_info = json.dumps(data_param, ensure_ascii=False)
        param.type = ADSqliteService.check_type(extend_info.get("type"))
        param.create_time = "0"
        param.modify_time = "0"
        name_length = len(param.name)
        distinguishedname = data_param.get("CanonicalName", "")
        distinguishedname = distinguishedname.replace("\\\\", "\\")
        distinguishedname = distinguishedname.replace("\\/", "/")
        distinguishedname_length = len(distinguishedname)
        if param.type == "d":
            parent_id_map["/" + distinguishedname] = param.uuid
        if distinguishedname_length > name_length:
            param.parent_path = "/" + distinguishedname[:distinguishedname_length - name_length][:-1]
            param.parent_uuid = ADSqliteService.get_parent_guid(param.parent_path, parent_id_map)
        return param

    @staticmethod
    def get_parent_guid(name, parent_id_map):
        parent_id = parent_id_map.get(name, "")
        return parent_id

    @staticmethod
    def get_ad_object_json_info(path, obj_list, object_from):
        sqlite_db_name = "copymetadata.sqlite"
        json_list = list()
        for obj in obj_list:
            # 对名称中有单引号的字符串做特殊处理
            obj = obj.replace("'", "''")
            db_path = os.path.join(path, sqlite_db_name)
            conn = sqlite3.connect(db_path)
            if object_from == '1':
                obj = json.loads(obj)
                common_name = obj.get('name')
                log.info(f"debug: nm: {common_name}")
                search_sql_cmd = f"SELECT * FROM T_COPY_METADATA where NAME=='{common_name}'"
            else:
                search_sql_cmd = f"SELECT * FROM T_COPY_METADATA where EXTEND_INFO=='{obj}'"
            get_cursor = conn.execute(search_sql_cmd)
            conn.commit()
            obj_tuple_list = get_cursor.fetchall()
            log.info(f"the obj_tuple_list get from sqlite is {obj_tuple_list}")
            for obj_tuple in obj_tuple_list:
                # 从数据库的第9列（即obj_tuple[8]）获取type与JSON中比较
                if object_from == '1' and obj.get('type') == json.loads(obj_tuple[8]).get('type'):
                    # 将数据库的第10列（obj_tuple[9]）加入到list
                    obj_info = json.loads(obj_tuple[9])
                    json_list.append(obj_info)
                elif obj_tuple_list and object_from == '0':
                    obj_info = json.loads(obj_tuple[9])
                    json_list.append(obj_info)
        log.info(f"json_list to restore is : {json_list}")
        return json_list

    @staticmethod
    def check_type(obj_type):
        # 判断对象是文件还是目录
        if obj_type in dir_type_list:
            return "d"
        return "f"
